package org.hugoduncan.appam.component;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.servlet.ServletException;
import javax.xml.namespace.QName;

import org.apache.abdera.model.Document;
import org.apache.abdera.model.Element;
import org.apache.abdera.model.Service;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hugoduncan.appam.model.CollectionComponent;
import org.hugoduncan.appam.model.Constants;
import org.hugoduncan.appam.model.ServiceComponent;
import org.hugoduncan.appam.model.WorkspaceComponent;
import org.hugoduncan.appam.server.AppServletRequest;
import org.hugoduncan.appam.server.AppServletResponse;

/** A ServiceComponent that uses a BasicWorkspace
 *
 * @author duncanhugo
 */
public class BasicService
    implements ServiceComponent
{
    private static final Log log = LogFactory.getLog(BasicService.class);
    private static final QName acceptQName=new QName("http://purl.org/atom/app#","accept","app");
    private String introspectionPath="/service";
    private Service service;

    private Collection<WorkspaceComponent> workspaces=
        new ArrayList<WorkspaceComponent>();

    public void setService(Service service) {
        this.service = service;
    }

    public void setIntrospectionPath(String introspectionPath) {
        this.introspectionPath = introspectionPath;
    }

    public String getIntrospectionPath() {
        return introspectionPath;
    }

    public boolean process(AppServletRequest request,
                           AppServletResponse response)
        throws IOException, ServletException
    {
        log.info("Service processing "+request.getMethod()+
                 " request to "+request.getPathInfo());
        if (request.getPathInfo().equals(introspectionPath))
            return sendIntrospectionDocument(request, response);
        for (WorkspaceComponent workspace:workspaces)
            if (process(workspace,request,response))
                return true;
        return false;
    }

    private boolean process(WorkspaceComponent workspace,
                            AppServletRequest request,
                            AppServletResponse response)
        throws IOException, ServletException
    {
        if (workspace.getDefaultCollection().process(request, response))
            return true;
        if (workspace.getOtherCollections()!=null)
            for (CollectionComponent collection:workspace.getOtherCollections())
                if (collection.process(request, response))
                    return true;
        return false;
    }

    @SuppressWarnings("unchecked")
    protected boolean sendIntrospectionDocument(
            AppServletRequest request,
            AppServletResponse response)
    {
        // modify the introspection document, so that the request
        // path is reflected into the document
        try {
            service.setBaseUri(response.getBaseUrl());
        } catch (URISyntaxException e1) {
            log.error("could not set the base url for the service");
        }

        // turn the collection hrefs into absolute hrefs
        // This is an APE workaround for the moment
        debugWrite(this.service.getDocument());
        org.apache.abdera.model.Document<Service> serviceCopy=
            (Document<Service>) this.service.getDocument().clone();
        debugWrite(serviceCopy);

        Service service=serviceCopy.getRoot();
        for (org.apache.abdera.model.Workspace workspace:
                 service.getWorkspaces())
            for (org.apache.abdera.model.Collection collection:
                     workspace.getCollections())
                try {
                    //collection.setBaseUri(response.getBaseUrl());
                    collection.setHref(response.getBaseUrl()+
                                       collection.getHref());
                    org.apache.abdera.model.Collection c=
                        this.service.getCollection(workspace.getTitle(),
                                                   collection.getTitle());
                    if (c==null)
                    {
                        log.error("Couldn't get collection for ["+
                                  workspace.getTitle()+","+
                                  collection.getTitle());
                    }
                    else if (c.getFirstChild(acceptQName)!=null && c.getAccept()!=null)
                        collection.setAccept(c.getAccept());
                } catch (URISyntaxException e1) {
                    log.error("Could not set collection href");
                }

        if (log.isDebugEnabled())
        {
            StringWriter sw=new StringWriter();
            try {
                service.getDocument().writeTo(sw);
            } catch (IOException e) {
                log.error("Coldn't produce debug output for introspection document");
            }
            log.debug("Introspection document sent");
            log.debug(sw.toString());
        }

        try {
            response.setCharacterEncoding("utf-8");
            response.setContentType(Constants.SC_APP_CONTENT_TYPE);

            PrintWriter writer=response.getWriter();
            service.getDocument().writeTo(writer);
        } catch (IOException e) {
            log.error("unexpected exception: ",e);
        }

        return true;
    }

    @SuppressWarnings("unchecked")
    public void setWorkspaces(List workspaces) {
        this.workspaces = workspaces;
    }

    public void add(WorkspaceComponent wsc) {
        workspaces.add(wsc);
    }

    private <T extends Element> void debugWrite(Document<T> document)
    {
        try {
            StringWriter sw=new StringWriter();
            document.writeTo(sw);
            log.debug(sw.toString());
        } catch (IOException e1) {
            log.error("Could not output document",e1);
        }
    }
}
